Certain server-based applications can be executed by multiple users on one or more servers across a network. For example, rather than running individual applications (e.g., individual spreadsheet applications) on desktop computers, multiple users can access a server over an Intranet through their desktop computers and execute a server-based application (e.g., a server-based spreadsheet application) on that server. Through this server-based application, multiple users can manipulate common functionality and data available on that server.
A typical scenario might include a server-based accounting program that is accessed by multiple accountants from various enterprise departments. The server-based application receives user requests, creates and accesses data objects, performs appropriate computations on these data objects, and renders application-specific presentations of the computation results for display on the users' desktop computers (e.g., through browsers). Furthermore, to avoid the time and resources required to build data objects for each user, once these server-based objects are created they can be shared among multiple users (e.g., in servicing multiple requests).
However, creation of certain complex data objects for a server-based application can be time-consuming and CPU-consuming. For example, in a server-based spreadsheet application, a user may request that a workbook be created for display on his or her desktop computer. In response to the request, the server-based application performs a slow process of building one or more individual worksheet objects and adding them to a “collection” that represents the workbook. The objects can then be rendered for display on the user's system. As they are being built and added to the collection, however, the worksheets objects and/or their contents may be retrieved from various, possibly remote, storage locations. Therefore, the process of building each worksheet object can take a noticeably long amount of time from a user's perspective.
When multiple users (e.g., represented by multiple threads) attempt to access data objects in the same collection, multi-threaded applications synchronize data access to the collection by the multiple threads in order to ensure that an access by one thread does not corrupt an access by another thread. One method of synchronization used in traditional multi-threaded applications is a simple locking mechanism, wherein a first thread that is accessing a data object of the collection takes control of the collection and locks out all other threads. The lock prevents access by any other thread until all of the first thread's accesses to that collection are completed. Thus, if a second thread attempts to access a different data object in the collection during the locked period, access by the second thread is blocked. As a result, the second thread waits unproductively until the collection is available. Accordingly, while simple locking mechanisms can be used in typical multi-threaded applications to synchronize multi-threaded access, these same mechanisms prove too slow for collections in many multi-threaded server-based applications.